﻿using System;
using System.Buffers.Binary;
using System.Buffers.Text;
using System.Runtime.InteropServices;
using System.Text;

namespace WholeSlideImageLibrary.Formats.Dmetrix
{
    internal struct DmetrixFileHeader
    {
        /// <summary>
        /// Company. (7 bytes, "Dmetrix")
        /// </summary>
        public string Company { readonly get; private set; }
        /// <summary>
        /// Whether this file is encrypted.
        /// </summary>
        public bool IsEncrypted { readonly get; private set; }
        /// <summary>
        /// Sanner device. (10 bytes)
        /// </summary>
        public string Device { readonly get; private set; }
        /// <summary>
        /// Craetion date. (8 bytes)
        /// </summary>
        public DateTime Date { readonly get; private set; }
        /// <summary>
        /// Image width.
        /// </summary>
        public int Width { readonly get; private set; }
        /// <summary>
        /// Image height.
        /// </summary>
        public int Height { readonly get; private set; }
        /// <summary>
        /// TODO: Unknown property.
        /// </summary>
        public int HSize { readonly get; private set; }
        /// <summary>
        /// TODO: Unknown property.
        /// </summary>
        internal long TSize { readonly get; private set; }
        /// <summary>
        /// Max layer.
        /// </summary>
        public short MaxLayer { readonly get; private set; }
        /// <summary>
        /// Microns per pixel in X direction.
        /// </summary>
        public double XLength { readonly get; private set; }
        /// <summary>
        /// Microns per pixel in Y direction.
        /// </summary>
        public double YLength { readonly get; private set; }
        /// <summary>
        /// Magnification of this image.
        /// </summary>
        public int Multple { readonly get; private set; }
        /// <summary>
        /// Indexes to each layer index block. (20 blocks)
        /// </summary>
        public DmetrixFileLayerIndex[] LayerIndexes { readonly get; private set; }


        internal const int MarshalStructureBaseSize = 68;
        internal const int MarshalStructureSize = MarshalStructureBaseSize + 20 * DmetrixFileLayerIndex.MarshalStructureSize;

        public static unsafe bool TryParse(ReadOnlySpan<byte> buffer, out DmetrixFileHeader data)
        {
            data = default;
            if (buffer.Length < MarshalStructureSize)
            {
                return false;
            }
#if !NO_FAST_SPAN
            data.Company = Encoding.UTF8.GetString(buffer.Slice(0, 7));
            if (!data.Company.Equals("Dmetrix", StringComparison.Ordinal))
            {
                return false;
            }
            data.IsEncrypted = buffer[7] == 69;
            data.Device = Encoding.UTF8.GetString(buffer.Slice(8, 10));
            if (!Utf8Parser.TryParse(buffer.Slice(18, 4), out int year, out _) ||
                !Utf8Parser.TryParse(buffer.Slice(22, 2), out int month, out _) ||
                !Utf8Parser.TryParse(buffer.Slice(24, 2), out int day, out _))
            {
                return false;
            }
            data.Date = new DateTime(year, month, day);
#else
            fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
            {
                data.Company = Encoding.UTF8.GetString(pointer, 7);
                if (!data.Company.Equals("Dmetrix", StringComparison.Ordinal))
                {
                    return false;
                }
                data.IsEncrypted = pointer[7] == 69;
                data.Device = Encoding.UTF8.GetString(&pointer[8], 10);
                string date = Encoding.UTF8.GetString(&pointer[18], 8);
                if (!int.TryParse(date.Substring(0, 4), out int year) ||
                    !int.TryParse(date.Substring(4, 2), out int month) ||
                    !int.TryParse(date.Substring(6, 2), out int day))
                {
                    return false;
                }
                data.Date = new DateTime(year, month, day);
            }
#endif
            data.Width = BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(26));
            data.Height = BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(30));
            data.HSize = BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(34));
            data.TSize = BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(38));
            data.MaxLayer = BinaryPrimitives.ReadInt16LittleEndian(buffer.Slice(46));
            data.XLength = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(48)));
            data.YLength = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice(56)));
            data.Multple = BinaryPrimitives.ReadInt32LittleEndian(buffer.Slice(64));
            ReadOnlySpan<byte> indexesData = buffer.Slice(MarshalStructureBaseSize);

            var indexes = new DmetrixFileLayerIndex[20];
            for (int i = 0; i < 20; i++)
            {
                if (!DmetrixFileLayerIndex.TryParse(indexesData.Slice(i * DmetrixFileLayerIndex.MarshalStructureSize), out indexes[i]))
                {
                    return false;
                }
            }
            data.LayerIndexes = indexes;

            return true;
        }

    }
}
